package cluster

import (
	"context"
	"fmt"
	"os"
	"path"
	"strings"
	"text/tabwriter"

	configv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	yaml2 "sigs.k8s.io/yaml"

	"gopkg.openfuyao.cn/bkeadm/pkg/executor/k8s"
	"gopkg.openfuyao.cn/bkeadm/pkg/global"
	"gopkg.openfuyao.cn/bkeadm/pkg/root"
	"gopkg.openfuyao.cn/bkeadm/utils/log"
)

type Options struct {
	root.Options
	Args      []string `json:"args"`
	File      string   `json:"file"`
	Conf      string   `json:"Conf"`
	NtpServer string   `json:"ntpServer"`
}

var gvr = schema.GroupVersionResource{
	Group:    configv1beta1.GVK.Group,
	Version:  configv1beta1.GVK.Version,
	Resource: "bkeclusters",
}

func (op *Options) ClusterPre() (err error) {
	// k8s client
	if global.K8s == nil {
		global.K8s, err = k8s.NewKubernetesClient(op.KubeConfig)
		if err != nil {
			return err
		}
	}
	return nil
}

func (op *Options) List() {
	var workloadUnstructured *unstructured.UnstructuredList
	var err error
	dynamicClient := global.K8s.GetDynamicClient()
	workloadUnstructured, err = dynamicClient.Resource(gvr).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		log.Error(err.Error())
		return
	}
	bclusterlist := &configv1beta1.BKEClusterList{}
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(workloadUnstructured.UnstructuredContent(), bclusterlist)
	if err != nil {
		log.Error(err.Error())
		return
	}

	headers := []string{"namespace", "name", "endpoint", "master", "worker", "pause", "dryRun", "reset"}
	var rows [][]string
	for _, bc := range bclusterlist.Items {
		master := 0
		worker := 0
		//if bc.Status.ClusterStatus != nil {
		//	for _, node := range bc.Status. {
		//		if utils.ContainsString(node.Role, "master") {
		//			master += 1
		//		}
		//		worker += 1
		//	}
		//}
		line := []string{
			bc.Namespace,
			bc.Name,
			fmt.Sprintf("%s:%d", bc.Spec.ControlPlaneEndpoint.Host, bc.Spec.ControlPlaneEndpoint.Port),
			fmt.Sprintf("%d", master),
			fmt.Sprintf("%d", worker),
			fmt.Sprintf("%t", bc.Spec.Pause),
			fmt.Sprintf("%t", bc.Spec.DryRun),
			fmt.Sprintf("%t", bc.Spec.Reset),
		}
		rows = append(rows, line)
	}
	const tabPadding = 2 // 列之间的最小空格数，用于tabwriter对齐
	// 使用tabwriter输出表格
	w := tabwriter.NewWriter(os.Stdout, 0, 0, tabPadding, ' ', 0)
	fmt.Fprintln(w, strings.Join(headers, "\t"))
	for _, row := range rows {
		fmt.Fprintln(w, strings.Join(row, "\t"))
	}
	err = w.Flush()
	if err != nil {
		fmt.Println("flush tablewriter failed:", err.Error())
	}
}

func (op *Options) Remove() {
	var workloadUnstructured *unstructured.Unstructured
	var err error
	ns := strings.Split(op.Args[0], "/")
	dynamicClient := global.K8s.GetDynamicClient()
	workloadUnstructured, err = dynamicClient.Resource(gvr).Namespace(ns[0]).Get(context.TODO(), ns[1], metav1.GetOptions{})
	if err != nil {
		log.Error(err.Error())
		return
	}
	bcluster := &configv1beta1.BKECluster{}
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(workloadUnstructured.UnstructuredContent(), bcluster)
	if err != nil {
		log.Error(err.Error())
		return
	}
	bcluster.Spec.Reset = true

	t1, err := runtime.DefaultUnstructuredConverter.ToUnstructured(bcluster)
	if err != nil {
		log.Error(err.Error())
		return
	}
	m1 := &unstructured.Unstructured{t1}
	_, err = dynamicClient.Resource(gvr).Namespace(bcluster.Namespace).Update(context.TODO(), m1, metav1.UpdateOptions{})
	if err != nil {
		log.Error(err.Error())
		return
	}
}

func (op *Options) Scale() {
	bkeCluster, err := NewBKEClusterFromFile(op.File)
	if err != nil {
		log.BKEFormat(log.ERROR, fmt.Sprintf("Failed to load the configuration file. %v", err))
		return
	}

	if !strings.HasPrefix(bkeCluster.Namespace, "bke") {
		bkeCluster.Namespace = "bke-" + bkeCluster.Namespace
	}

	by, err := yaml2.Marshal(bkeCluster)
	if err != nil {
		log.BKEFormat(log.ERROR, err.Error())
		return
	}

	_ = os.MkdirAll(path.Join(global.Workspace, "cluster"), 0644)
	bkeFile := path.Join(global.Workspace, "cluster", fmt.Sprintf("%s-%s.yaml", bkeCluster.Namespace, bkeCluster.Name))
	err = os.WriteFile(bkeFile, by, 0644)
	if err != nil {
		log.BKEFormat(log.ERROR, fmt.Sprintf("File generation failure %s", err.Error()))
		return
	}

	log.BKEFormat(log.INFO, fmt.Sprintf("Patch cluster-api yaml to the cluster"))
	err = global.K8s.PatchYaml(bkeFile, map[string]string{})
	if err != nil {
		log.BKEFormat(log.ERROR, fmt.Sprintf("Failed to install bke-cluster, %v", err))
		return
	}

	log.BKEFormat(log.INFO, "Waiting for the cluster to start...")
	global.K8s.WatchEventByAnnotation(bkeCluster.Namespace)
}

func (op *Options) Log() {
	ns := strings.Split(op.Args[0], "/")
	global.K8s.WatchEventByAnnotation(ns[0])
}

func (op *Options) ExistsCluster() {

	conf := &configv1beta1.BKECluster{}
	yamlFile, err := os.ReadFile(op.File)
	if err != nil {
		log.Error(err.Error())
		return
	}
	err = yaml2.Unmarshal(yamlFile, conf)
	if err != nil {
		log.Error(err.Error())
		return
	}
	if conf.Spec.ClusterConfig == nil {
		log.Error("The cluster configuration cannot be empty. ")
		return
	}

	namespace := corev1.Namespace{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "v1",
			Kind:       "Namespace",
		},
		ObjectMeta: metav1.ObjectMeta{
			Name: conf.Namespace,
		},
		Spec:   corev1.NamespaceSpec{},
		Status: corev1.NamespaceStatus{},
	}
	err = global.K8s.CreateNamespace(&namespace)
	if err != nil {
		log.Error(err.Error())
		return
	}

	kubeConfig, err := os.ReadFile(op.Conf)
	if err != nil {
		log.Error(err.Error())
		return
	}

	secret := corev1.Secret{
		TypeMeta: metav1.TypeMeta{
			APIVersion: "v1",
			Kind:       "Secret",
		},
		ObjectMeta: metav1.ObjectMeta{
			Namespace: conf.Namespace,
			Name:      conf.Name + "-kubeconfig",
		},
		Data: map[string][]byte{
			"value": kubeConfig,
		},
	}

	err = global.K8s.CreateSecret(&secret)
	if err != nil {
		log.Error(err.Error())
		return
	}
	err = global.K8s.InstallYaml(op.File, map[string]string{}, "")
	if err != nil {
		log.Error(err)
		return
	}
}
